/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.testis.testdriver.operations;

import org.testis.testdriver.conditions.ConditionEvaluatingService;
import org.testis.testdriver.validation.TestPartValidateContext;
import org.testis.testdriver.exceptions.TestAbortException;
import org.testis.testdriver.*;
import org.testis.testdriver.AtofEnumaration.AtofLogLevel;
import org.testis.testdriver.AtofEnumaration.DebuggingMode;
import org.testis.testdriver.validation.CriticalPostCaseException;
import java.util.LinkedList;

/**
 * This class is the TestPartOperation Decorator to iterate and execute child 
 * TestParts that belongs to the current TestPart.
 * 
 * @author barisergun
 */
public class TestPartOperationIterateChilds extends TestPartOperationDecorator {

    private TestPart partInUse;

    public TestPartOperationIterateChilds(TestPart testPart) {
        partInUse = testPart;

    }

    @Override
    public boolean runOperation(DebuggingMode mode) throws InterruptedException {
        if (super.runOperation(mode)) {
            boolean runResult = true;
            try {
                runResult = runSpecificBehaviour(mode);
            } catch (RuntimeException ex) {

                if (ex instanceof CriticalPostCaseException) {
                    partInUse.getParent().andTestPartResult(partInUse.getTestPartResult());

                }

                boolean isTestPlan = false;
                if (partInUse instanceof StepContainer) {
                    if (((StepContainer) partInUse).getContainerType()
                            == AtofEnumaration.TestPartType.TestPlan) {
                        isTestPlan = true;
                    }
                }

                boolean willThrowUp = false;
                if (isTestPlan) {
                    NotificationSubjectSingleton.getInstance().notifyLogMessages(
                            partInUse.getPartName(),
                            AtofLogLevel.ERROR,
                            "Exception occured:" + ex.getMessage(),
                            partInUse.getPartGuid());

                    if (ex instanceof TestAbortException) {

                        willThrowUp = true;

                        // Continue to thwor up for aborting.
                    }

                } else {

                    TestPartValidateContext easyContainer = new TestPartValidateContext(
                            partInUse);
                    // To be able to log the container result before throwing up.
                    easyContainer.validateFinalPartResult(
                            null,//Not needed for Suite Validation
                            null,//Not needed for Suite Validation
                            partInUse.getTestPartResult());

                    willThrowUp = true;

                }
                if (willThrowUp) {
                    throw ex;
                }


            }
            return runResult;
        } else {
            return false;
        }
    }

    /**
     * This method is used to drive the specific Tasks that belong to this decorator
     * operation.
     * 
     * @return boolean the result of operation. This result will affect the operation
     * of linked decorator tasks. 
     */
    private boolean runSpecificBehaviour(DebuggingMode curMode) throws
            InterruptedException {
        TestIterator innerIter = partInUse.createIter();
        innerIter.init();
        while (innerIter.isDone()) {

            checkExecutionIntervention();
            innerIter.next();
            TestPart curStepCont = innerIter.currentItem();
            
            ConditionEvaluatingService.evaluateConditions(curStepCont);

            TestPartValidateContext iterRun = new TestPartValidateContext(
                    curStepCont);

            boolean breakTheLoop = false;
            boolean curRun = false;
            boolean finalVal = false;

            if (curStepCont.getFailedConditions().isEmpty()) {
                curRun = driveDebuggingMode(curStepCont,
                        innerIter,
                        curMode);


                finalVal = iterRun.validatePartResult(innerIter,
                        curMode,
                        curRun);
            } else {//if (curStepCont.getFailedConditions().isEmpty()) {

                finalVal = true;// condition fails for this tree node so it
                // must be final validation
                curRun = true;// If condition fails consider the test part as
                // returning a true result.

            }
            if (finalVal) {
                if (!iterRun.validateFinalPartResult(innerIter,
                        curMode,
                        curRun)) {
                    TestPart postCheck = null;
                    while (innerIter.isDone()) {
                        innerIter.next();
                        postCheck = innerIter.currentItem();
                    }
                    if (postCheck instanceof StepContainer && postCheck.getContainerType()
                            == AtofEnumaration.TestPartType.PostConditionCase) {
                        innerIter.previous();
                    } else {
                        breakTheLoop = true;
                    }
                }

            }

            if (breakTheLoop) {
                break;
            }
        }

        if (partInUse.getContainerType() == AtofEnumaration.TestPartType.TestSuite) {
            TestPartValidateContext suiteVal = new TestPartValidateContext(
                    partInUse);
            // To be able to log the suite resukt
            suiteVal.validateFinalPartResult(
                    null,//Not needed for Suite Validation
                    null,//Not needed for Suite Validation
                    partInUse.getTestPartResult());
        }



        return true;
    }

    /**
     * This method is used to call the desired debugging mode drive from a Factory
     * Design Pattern.
     *
     * @param  ourTestpart  TestPart that is being executed.
     * @param  innerIt The child list iterator of the current Part..      
     * @param  mode The debugging mode of the current run.     
     * @param  containerLogId The unique uuid that the container logs will be related.     
     *      
     * @return boolean result of the TestPart execution
     */
    private boolean driveDebuggingMode(TestPart ourTestpart,
            TestIterator innerIt,
            AtofEnumaration.DebuggingMode mode)
            throws
            InterruptedException {
        DriverFactory ourDriverFact = new DriverFactory();
        ExecutionDriver ourDriver = ourDriverFact.getInstance(mode);
        return ourDriver.drive(ourTestpart,
                innerIt);

    }

    private void checkExecutionIntervention() throws InterruptedException {
        AtofEnumaration.ExecutionInterventionType interventVal =
                ExecutionInterventionStateSingleton.createInstance().
                getInterventionType();


        do {
            NotificationSubjectSingleton.createInstance(true).
                    notifyExecutionStatusToUsers(interventVal);
            if (interventVal
                    == AtofEnumaration.ExecutionInterventionType.Abort) {

                throw new TestAbortException(TestStringConstants.getAbortString());
            } else if (interventVal
                    == AtofEnumaration.ExecutionInterventionType.Resume) {
                //resumed
                break;
            }
            Thread.sleep(500);
            interventVal = ExecutionInterventionStateSingleton.getInstance().
                    getInterventionType();

        } while (interventVal
                == AtofEnumaration.ExecutionInterventionType.Pause);
        NotificationSubjectSingleton.getInstance().
                notifyExecutionStatusToUsers(interventVal);
    }
}
